package edu.mse.camel.client.posterboard.ui.outline;

import java.util.HashMap;
import java.util.Map;

import org.eclipse.draw2d.ColorConstants;
import org.eclipse.draw2d.Figure;
import org.eclipse.draw2d.LightweightSystem;
import org.eclipse.draw2d.MarginBorder;
import org.eclipse.draw2d.Viewport;
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.gef.EditPartViewer;
import org.eclipse.gef.GraphicalViewer;
import org.eclipse.gef.LayerConstants;
import org.eclipse.gef.RootEditPart;
import org.eclipse.gef.editparts.ScalableFreeformRootEditPart;
import org.eclipse.gef.ui.parts.GraphicalEditor;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.ISelectionProvider;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.widgets.Canvas;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.part.Page;
import org.eclipse.ui.views.contentoutline.IContentOutlinePage;

import edu.mse.camel.client.core.session.event.ClientInternalEvent;
import edu.mse.camel.client.eventbus.pubsub.EventManager;
import edu.mse.camel.client.eventbus.pubsub.ISubscriber;
import edu.mse.camel.client.events.EventIDs;
import edu.mse.camel.client.events.IEvent;
import edu.mse.camel.client.events.peer.CallForAttentionEvent;
import edu.mse.camel.client.events.peer.ViewportChangeEvent;
import edu.mse.camel.client.session.team.ITeamMember;
import edu.mse.camel.client.session.team.TEAMMEMBER_STATUS;
import edu.mse.camel.client.session.team.TeamList;

public class PosterBoardThumbnailPage extends Page 
implements IContentOutlinePage, ISubscriber {

	private Map<String, ViewportFigure> viewports;

	private PosterBoardScrollableThumbnail thumbnail;

	private LightweightSystem lightweightSystem;

	private Control control;

	private EditPartViewer graphicalViewer;

	final int width = Display.getDefault().getBounds().width;
	final int height = Display.getDefault().getBounds().height;
	
	/**
	 * Constructs a ContentOutlinePage for the given viewer.
	 * 
	 * @param viewer the viewer for which a thumbnail should be created.
	 * @param selectionSynchronizer 
	 */
	public PosterBoardThumbnailPage(EditPartViewer viewer) {
		assert(viewer != null);
		this.graphicalViewer = viewer;
		viewports = new HashMap<String, ViewportFigure>();
		EventManager.getInstance().subscribeTo(
				EventIDs.VIEWPORT_CHANGED_EVENT, this);
		EventManager.getInstance().subscribeTo(
				EventIDs.CLIENT_INTERNAL_EVENT, this);
		EventManager.getInstance().subscribeTo(
				EventIDs.CALL_FOR_ATTENTION, this);
	}

	/**
	 * Do nothing, because this object doesn't fire selectionChange.
	 * 
	 * @param listener 
	 * 
	 * @see ISelectionProvider#addSelectionChangedListener(ISelectionChangedListener)
	 */
	public void addSelectionChangedListener(ISelectionChangedListener listener) {

	}

	/**
	 * Forwards the createControl request to the editpartviewer.
	 * 
	 * @param parent 
	 * 
	 * @see org.eclipse.ui.part.IPage#createControl(org.eclipse.swt.widgets.Composite)
	 */
	@Override
	public void createControl(Composite parent) {	

		// An swt canvas drawing the figure.
		Canvas canvas = new Canvas(parent, SWT.NONE);
		// A draw2d object linkink swt and draw2d
		lightweightSystem = new LightweightSystem(canvas);

		RootEditPart rep = getGraphicalViewer().getRootEditPart();
		if (rep instanceof ScalableFreeformRootEditPart) {
			ScalableFreeformRootEditPart root = (ScalableFreeformRootEditPart) rep;
			thumbnail = new PosterBoardScrollableThumbnail(
					(Viewport) root.getFigure(),
					getSelfColor());
			thumbnail.setBorder(new MarginBorder(3));
			thumbnail.setSource(root.getLayer(LayerConstants.PRINTABLE_LAYERS));
			thumbnail.setFigureColor(getSelfColor());
			lightweightSystem.setContents(thumbnail);
			for (ViewportFigure v : viewports.values()) {
				thumbnail.add(v);
			}
		}
		control = canvas;
		
		if (TeamList.getInstance().getMyStatus().equals(TEAMMEMBER_STATUS.SESSION)) {
			addOtherViewports();
		}
		
	}

	private RGB getSelfColor() {
		if (TeamList.getInstance().getMyStatus().equals(TEAMMEMBER_STATUS.SESSION)) {
			return TeamList.getInstance().getMe().getColor();
		}
		return ColorConstants.menuBackgroundSelected.getRGB();
	}

	/**
	 * Dispose underlying objects.
	 */
	@Override
	public void dispose() {
		// deactivate the thumbnail
		if (thumbnail != null) {
			thumbnail.deactivate();
			thumbnail = null;
		}
		viewports.clear();
		EventManager.getInstance().unSubscribeTo(
				EventIDs.VIEWPORT_CHANGED_EVENT, this);
		EventManager.getInstance().unSubscribeTo(
				EventIDs.CLIENT_INTERNAL_EVENT, this);
		EventManager.getInstance().subscribeTo(
				EventIDs.CALL_FOR_ATTENTION, this);
		super.dispose();
	}

	private EditPartViewer getGraphicalViewer() {
		return graphicalViewer;
	}

	public void changeGraphicalViewer(EditPartViewer viewer) {
		graphicalViewer = viewer;

		RootEditPart rep = viewer.getRootEditPart();
		if (rep instanceof ScalableFreeformRootEditPart) {
			ScalableFreeformRootEditPart root = (ScalableFreeformRootEditPart)rep;
			// To force revalidation
			thumbnail.setDirty(true);
			// set new figure and viewport
			thumbnail.setViewport((Viewport) root.getFigure());
			thumbnail.setSource(root.getLayer(LayerConstants.PRINTABLE_LAYERS));
		}
	}

	protected void setMyViewportColor(RGB rgb) {
		thumbnail.setFigureColor(rgb);
	}

	/**
	 * @param editor
	 */
	public void changeGraphicalEditor(GraphicalEditor editor) {
		GraphicalViewer viewer = (GraphicalViewer)editor.getAdapter(GraphicalViewer.class);
		changeGraphicalViewer(viewer);
	}

	@Override
	public Control getControl() {
		return control;
	}

	/* (non-Javadoc)
	 * @see org.eclipse.jface.viewers.ISelectionProvider#getSelection()
	 */
	public ISelection getSelection() {
		return StructuredSelection.EMPTY;
	}


	/* (non-Javadoc)
	 * @see org.eclipse.jface.viewers.ISelectionProvider#removeSelectionChangedListener(org.eclipse.jface.viewers.ISelectionChangedListener)
	 */
	public void removeSelectionChangedListener(ISelectionChangedListener listener) {

	}

	/**
	 * Sets focus to a part in the page.
	 */
	@Override
	public void setFocus() {
		if (getControl() != null) {
			getControl().setFocus();
		}
	}


	/* (non-Javadoc)
	 * @see org.eclipse.jface.viewers.ISelectionProvider#setSelection(org.eclipse.jface.viewers.ISelection)
	 */
	public void setSelection(ISelection selection) {
		if(selection instanceof IStructuredSelection) {
			IStructuredSelection sel = (IStructuredSelection)selection;
			Object editor = sel.getFirstElement();
			if( editor instanceof GraphicalEditor) {
				changeGraphicalEditor((GraphicalEditor)editor);
			}
		}
	}

	@Override
	public void notifyEvent(IEvent e) {
		if (thumbnail == null) {
			return;
		}
		if (e instanceof ViewportChangeEvent) {
			ViewportChangeEvent vc = (ViewportChangeEvent) e;
			int pbViewHeight = thumbnail.targetSize.height;
			final Rectangle rect = new Rectangle((int) (vc.x*pbViewHeight), (int) (vc.y*pbViewHeight), (int) (vc.width*pbViewHeight), (int) (vc.height*pbViewHeight));
			final Figure f = viewports.get(vc.userID);
			Display.getDefault().asyncExec(new Runnable() {

				@Override
				public void run() {
					if (f != null) {
						f.setBounds(rect);
					}
				}

			});
		} else if (e instanceof ClientInternalEvent) {
			//If I am not in session then set all defaults
			if (!TeamList.getInstance().getMyStatus()
							.equals(TEAMMEMBER_STATUS.SESSION)) {
				if (thumbnail != null) {
					thumbnail.setFigureColor(getSelfColor());
					removeOtherViewports();
				}
			} else {
				addOtherViewports();
			}
		} else if (e instanceof CallForAttentionEvent) {
			CallForAttentionEvent call = (CallForAttentionEvent) e;
			final Figure f = viewports.get(call.getCallingUser());
			ViewportRendererThread t = new ViewportRendererThread(f);
			t.start();			
		}
	}

	private void removeOtherViewports() {
		for (final Figure f : viewports.values()) {
			Display.getDefault().syncExec(new Runnable() {

				@Override
				public void run() {
					if (thumbnail.getChildren().contains(f)) {
						thumbnail.remove(f);
					}
				}

			});
		}
		viewports.clear();
	}

	private void addOtherViewports() {
		//The posterboard outline view may not be open when the user logged in
		//so we have to first check if we have logged in or not
		if (TeamList.getInstance().getMyStatus().equals(TEAMMEMBER_STATUS.OFFLINE)) {
			return;
		}
		
		if (thumbnail != null) {
			thumbnail.setFigureColor(getSelfColor());
		}
		
		for (final String anID : viewports.keySet()) {
			boolean found = false;
			for (ITeamMember member : TeamList.getInstance().getSessionTeamMembers()) {
				String memberID = member.getName();
				if (anID.equals(memberID)) {
					found = true;
				}
			}
			if (!found) {
				//remove the viewport
				final ViewportFigure vf = viewports.get(anID);
				Display.getDefault().syncExec(new Runnable() {
				
					@Override
					public void run() {
						if (thumbnail != null
								&& vf != null) {
							if (thumbnail.getChildren().contains(vf)) {
								//becuase we could multiple calls here because of the
								//Client internal event we could have already removed
								//the figure
								thumbnail.remove(vf);
							}
						}
				
					}
				});
			}
		}
		
		for (ITeamMember member : TeamList.getInstance().getSessionTeamMembers()) {
			String memberID = member.getName();
			RGB rgb = member.getColor(); 
			
			ViewportFigure vf = viewports.get(memberID);
			if (vf == null) {
				if(rgb != null) {
					vf = new ViewportFigure(rgb);
				} else {//by default create a red viewport for others
					vf = new ViewportFigure(ColorConstants.red.getRGB());
				}
				viewports.put(memberID, vf);
			}

			//just so that we can pass to the ASyncExec
			final ViewportFigure f = viewports.get(memberID);
			Display.getDefault().syncExec(new Runnable() {

				@Override
				public void run() {
					if (thumbnail != null && f != null) {
						thumbnail.add(f);
					}
				}

			});
		}
	}
	
	static private class ViewportRendererThread extends Thread {

		private Figure viewport;
		
		public ViewportRendererThread(Figure viewport) {
			this.viewport = viewport;
		}
		
		@Override
		public void run() {
			try {
				for ( int i=0 ; i<20 ; ++i) {
				
					Display.getDefault().syncExec(new Runnable() {
		
						@Override
						public void run() {
							if (viewport != null) {
								viewport.setVisible(false);								
							}
						}
		
					});
					
					sleep(500);
					
					Display.getDefault().syncExec(new Runnable() {
						
						@Override
						public void run() {
							if (viewport != null) {
								viewport.setVisible(true);
							}
						}
		
					});
					
					sleep (500);
					
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		
		
		
	}
	
}
